﻿using System;
using System.IO;
using System.Net;
using System.Runtime.Remoting.Messaging;
using System.Threading;
using System.Windows.Forms;

namespace WindowsFormsApp1
{
    /// <summary>
    /// .net 1.0 异步编程模型 APM （BeginInvoke, EndInvoke）
    /// </summary>
    public partial class Form_Async_APM2 : Form
    {
        public Form_Async_APM2()
        {
            InitializeComponent();
            txbUrl.Text = "http://k3.chunk.cn:8053/POSReport20/File/%E5%8D%9520240101_1217_1218171322608.xlsx";
        }

        private delegate string AsyncMethodCaller(string fileurl);

        SynchronizationContext sc;

        private void btnDownLoad_Click(object sender, EventArgs e)
        {
            rtbState.Text = "下载中……";
            btnDownLoad.Enabled = false;
            if (txbUrl.Text == string.Empty)
            {
                MessageBox.Show("请先输入下载地址！");
                return;
            }

            sc = SynchronizationContext.Current;                    // 获得调用线程的同步上下文对象
            AsyncMethodCaller methodCaller = new AsyncMethodCaller(DownLoadFileAsync);
            methodCaller.BeginInvoke(txbUrl.Text.Trim(), GetResult, null);
        }

        public string DownLoadFileAsync(string url)
        {
            int BufferSize = 2048;
            byte[] BufferRead = new byte[BufferSize];
            string savepath = Environment.GetFolderPath(Environment.SpecialFolder.Desktop) + "\\download_file.xlsx";  // dotNetFx 35setup.exe
            FileStream filestream = null;
            HttpWebResponse myWebResponse = null;
            if (File.Exists(savepath))
            {
                File.Delete(savepath);
            }

            filestream = new FileStream(savepath, FileMode.OpenOrCreate);
            try
            {
                HttpWebRequest myHttpWebRequest = (HttpWebRequest)WebRequest.Create(url);
                if (myHttpWebRequest != null)
                {
                    myWebResponse = (HttpWebResponse)myHttpWebRequest.GetResponse();
                    Stream responseStream = myWebResponse.GetResponseStream();
                    int readSize = responseStream.Read(BufferRead, 0, BufferSize);
                    while (readSize > 0)
                    {
                        filestream.Write(BufferRead, 0, readSize);
                        readSize = responseStream.Read(BufferRead, 0, BufferSize);
                    }
                }

                return string.Format("文件下载完成，文件大小为:{0}, 下载路径为：{1}", filestream.Length, savepath);
            }
            catch (Exception e)
            {
                return string.Format("下载过程中发生异常，异常信息为: {0}", e.Message);
            }
            finally
            {
                if (myWebResponse != null)
                {
                    myWebResponse.Close();
                }
                if (filestream != null)
                {
                    filestream.Close();
                }
            }
        }

        private void GetResult(IAsyncResult result) // 异步操作完成时需要执行的方法
        {
            AsyncMethodCaller caller = (AsyncMethodCaller)((AsyncResult)result).AsyncDelegate;
            string returnstring = caller.EndInvoke(result);
            sc.Post(ShowState, returnstring);
        }

        private void ShowState(object result)
        {
            rtbState.Text = result.ToString();
            btnDownLoad.Enabled = true;
        }


        //在用户单击下载按钮后，以上代码将该按钮设置为了不可用状态，避免用户重复单击该按钮发送文件下载请求。

        //接着，通过SynchronizationContext的Current属性获得UI线程的同步上下文对象。出于安全考虑，.NET规定控件只能被创建它的线程访问，而此时下载文件的操作正在另外一个线程中执行，故不能在该线程中访问UI线程的控件。

        //所以，此时要显示文件下载完成的状态信息，必须通过SynchronizationContext对象的Post方法，把显示状态信息的代码推送给UI线程去执行。如果在非UI线程访问控件，则会出现“不能跨线程访问控件”的异常。

        //最后，通过调用委托对象的BeginInvoke方法来进行异步的文件下载操作。下载完成时，将回调GetResult方法来获得操作结果。
    }
}
